﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Security;
using System.Collections.Concurrent;
using System.Collections;

using System.Drawing;
//using System.Management.Automation;
using System.Dynamic;
using System.Windows.Forms;
using System.Threading;
using System.Net;
using Newtonsoft.Json;
using System.Diagnostics;



namespace App2Wpf.Forms
{
    class ViewerControl
    {
        public string ServerAddress { get; set; } = "127.0.0.1";
        public IPAddress Server { get; set; }

        public nameip wininfo { get; set; }

        public int ServerPort { get; set; } = 8021;

        public bool UseTLS1_3 { get; set; }

        public bool DisableVerbosity { get; set; }

        public SecureString SecurePassword { get; set; }

        public String Password { get; set; }

        public ClipboardMode Clipboard { get; set; } = ClipboardMode.Both;

        public int ImageCompressionQuality { get; set; } = 75;

        public bool Resize { get; set; }

        public int ResizeRatio { get; set; } = 90;

        public bool AlwaysOnTop { get; set; }

        public PacketSize packetSize { get; set; } = PacketSize.Size9216;

        public BlockSize blockSize { get; set; } = BlockSize.Size64;

        public bool LogonUI { get; set; }

        public List<PSCustomObject> runspaces { get; set; }

        public clientViewerSession session { get; set; }

        public remoteWindow VirtualDeskForm { get; set; }

        public Hashtable virtualDesktopSyncHash { get; set; }
        public Dictionary<int, remoteWindow> virtualDesktopDic { get; set; }

        public ConcurrentDictionary<string, remoteWindow> virtualDesktopSyncDic { get; set; } = new ConcurrentDictionary<string, remoteWindow>();

        public ClientIO outputEventIO { get; set; }

        public Hashtable outputEventSyncHash { get; set; }
        public ConcurrentDictionary<string, ClientIO> outputEventSyncDic { get; set; } = new ConcurrentDictionary<string, ClientIO>();



        public Hashtable HostSyncHash { get; set; } = new Hashtable();





        public ViewerControl(IPAddress Server, nameip wininfo)
        {
            this.Server = Server;
            this.wininfo = wininfo;
            try
            {
                init(Server);
            }
            catch (Exception)
            {

                throw;
            }
            

            string host = "";
            string ClipboardText = "";
            HostSyncHash.Add("host", host);
            HostSyncHash.Add("ClipboardText", ClipboardText);

            HostSyncHash = Hashtable.Synchronized(HostSyncHash);
        }

        public void init(IPAddress server)
        {

            //Session successfully established, start streaming...
            this.session = new clientViewerSession();
            this.session.Server = server;
            session.OpenSession();
            session.ConnectDesktopWorker();
            session.ConnectEventsWorker();

            //Session successfully established, start streaming...

            //Create WinForms Environment...

            this.VirtualDeskForm = new remoteWindow();
            this.VirtualDeskForm.contextMenuStrip1.Dispose();                     //去掉弹出菜单
            this.VirtualDeskForm.FormBorderStyle = FormBorderStyle.FixedSingle;   //form 
            this.VirtualDeskForm.MaximizeBox = false;
            this.VirtualDeskForm.KeyPreview = true;

            this.VirtualDeskForm.Send += new remoteWindow.SendSingal(VirtualDeskFormIO_closing);
            this.VirtualDeskForm.Shown += logic_FormShown;
            (VirtualDeskForm.pictureBox1 as Control).MouseDown += logic_MouseDown;
            (VirtualDeskForm.pictureBox1 as Control).MouseUp += logic_MouseUp;
            //(VirtualDeskForm.pictureBox1 as Control).MouseMove += logic_MouseMove;
            (VirtualDeskForm.pictureBox1 as Control).MouseWheel += logic_MouseWheel;
            //(VirtualDeskForm.pictureBox1 as Control).DoubleClick += logic_DoubleClick;

            (VirtualDeskForm as Control).KeyPress += logic_KeyPress;
            //(VirtualDeskForm as Control).KeyUp += logic_KeyUp;
            (VirtualDeskForm as Control).KeyDown += logic_KeyDown;
            //(VirtualDeskForm.pictureBox1 as Control).Leave += logic_Leave;

            //this.virtualDesktopDic = new Dictionary<int, remoteWindow>();
            //virtualDesktopDic.Add(0, VirtualDeskForm);

            //var tempHashtable = new Hashtable();
            //tempHashtable.Add("VirtualDesktop", VirtualDeskForm);
            //var virtualDesktopSyncHash = Hashtable.Synchronized(tempHashtable);
            virtualDesktopSyncDic.GetOrAdd("DeskForm", VirtualDeskForm);

            //var outputEvent = new Hashtable();
            //outputEvent.Add("Writer", session.ClientEvents.Writer);
            //outputEventSyncHash = Hashtable.Synchronized(outputEvent);
            outputEventSyncDic.GetOrAdd("outputEvent", session.ClientEvents);

            VirtualDeskForm.Text = string.Format(
                "{0} {1}[{2}x{3}]:{4}/{5}-{6}",
                wininfo.name,
                wininfo.ip,
                session.ViewerConfiguration.VirtualDesktopWidth,
                session.ViewerConfiguration.VirtualDesktopHeight,
                //session.ServerInformation.Version,
                session.ServerInformation.Username,
                session.ServerInformation.MachineName,
                session.ServerInformation.WindowsVersion);

            //new window ClientSize
            VirtualDeskForm.ClientSize = new Size(
                session.ViewerConfiguration.VirtualDesktopWidth,
                session.ViewerConfiguration.VirtualDesktopHeight);

            //  Create a thread-safe hashtable to send events to remote server.

            //Hashtable outputable = new Hashtable();
            //outputable.Add("Writer", session.ClientEvents.Writer);
            //var outputEventSyncHash = Hashtable.Synchronized(outputable);

            if (!session.ServerInformation.ViewOnly)
            {

            }

            VirtualDesktopUpdater(session.ClientDesktop,
                virtualDesktopSyncDic,
                session.ViewerConfiguration,
                session.PacketSize);

            IngressEventUpdater(session.ClientEvents, virtualDesktopSyncDic, Clipboard);

            //closeFORM  -- 无法写入已关闭的TextWriter
            //EgressEventUpdater(virtualDesktopSyncHash, Clipboard);
        }


        public void VirtualDesktopUpdater(ClientIO Client,
                ConcurrentDictionary<string ,remoteWindow> virtualDesktopSyncDic,
                clientViewerConfiguration ViewerConfiguration,
                PacketSize packetSize)
        {

            byte[] mstruct = new byte[sizeof(Int32) * 3];
            System.IO.MemoryStream mstream = new System.IO.MemoryStream();

            Point destPoint = new Point(0, 0);

            Bitmap scene = new Bitmap(ViewerConfiguration.RemoteDesktopWidth, ViewerConfiguration.RemoteDesktopHeight);
            Graphics sceneGraphics = Graphics.FromImage(scene);
            sceneGraphics.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
            //virtualDesktopSyncHash["VirtualDesktop"].Picture.Image = scene;
            //virtualDesktopDic[0].pictureBox1.Image = scene;
            VirtualDeskForm.pictureBox1.Image = scene;
            //while (!virtualDesktopDic[0].Form.Visible)
            //{
            //    Thread.Sleep(100);

            //}

            //Tiny hack to correctly bring to front window, this is the most effective technique so far.
            //VirtualDesktopSyncHash.VirtualDesktop.Form.TopMost = true;
            //VirtualDesktopSyncHash.VirtualDesktop.Form.TopMost = false;

            Task DesktopUpdater = new Task(() =>
            {
                while (true)
                {
                    try
                    {
                        Client.SSLStream.Read(mstruct, 0, mstruct.Length);

                        long totalBufferSize = System.Runtime.InteropServices.Marshal.ReadInt32(mstruct, 0x0);

                        destPoint.X = System.Runtime.InteropServices.Marshal.ReadInt32(mstruct, 0x4);
                        destPoint.Y = System.Runtime.InteropServices.Marshal.ReadInt32(mstruct, 0x8);

                        mstream.SetLength(totalBufferSize);

                        mstream.Position = 0;

                        while (mstream.Position != mstream.Length)
                        {
                            long bufferSize = mstream.Length - mstream.Position;
                            if (bufferSize > (long)packetSize)
                            {
                                bufferSize = (long)packetSize;
                                int mbufferSize = (int)packetSize;

                            }

                            mstream.Write(Client.BinaryReader.ReadBytes((int)bufferSize), 0, (int)bufferSize);
                        }


                        if (mstream.Length == 0)
                        {
                            continue;
                        }

                        ///# Next Iterations
                        sceneGraphics.DrawImage(
                                System.Drawing.Image.FromStream(mstream),
                                destPoint
                            );

                        //VirtualDesktopSyncHash.VirtualDesktop.Picture.Invalidate();
                        //virtualDesktopDic[0].pictureBox1.Invalidate();
                        virtualDesktopSyncDic["DeskForm"].pictureBox1.Invalidate();

                    }
                    catch (Exception e)
                    {
                        clientFunc.Showlog(e.Message);
                        break;
                        //throw;
                    }

                    //finally
                    //{
                    //    if (scene != null)
                    //    {
                    //        scene.Dispose();
                    //    }

                    //    if (sceneGraphics != null)
                    //    {
                    //        sceneGraphics.Dispose();
                    //    }

                    //    if (mstream != null)
                    //    {
                    //        mstream.Close();
                    //    }
                    //}
                }
            }


            //VirtualDesktopSyncHash.VirtualDesktop.Close();

            );
            DesktopUpdater.Start();



        }

        public void IngressEventUpdater(ClientIO Client, 
            ConcurrentDictionary<string, remoteWindow> virtualDesktopSyncDic,
            ClipboardMode Clipboard)
        {
            Task IngressEventUpdater = new Task(() =>
            {
                while (true)
                {
                    try
                    {
                        var jsonEvent = Client.Reader.ReadLine();
                    }
                    catch (Exception)
                    {

                        //throw;
                        break;
                    }
                    try
                    {
                        var jsonEvent = Client.Reader.ReadLine();
                        SEvent jsonobj = (SEvent)JsonConvert.DeserializeObject(jsonEvent, typeof(SEvent));
                        switch ((InputEvent)jsonobj.Id)
                        {
                            case InputEvent.MouseCursorUpdated:
                                {
                                    if (jsonobj.Cursore != null)
                                    {
                                        Cursor newcur = Cursors.Arrow;
                                        switch ((CursorType)jsonobj.Cursore)
                                        {
                                            case CursorType.IDC_APPSTARTING:
                                                newcur = Cursors.AppStarting;
                                                break;
                                            case CursorType.IDC_ARROW:
                                                newcur = Cursors.Arrow;
                                                break;
                                            case CursorType.IDC_CROSS:
                                                newcur = Cursors.Cross;
                                                break;
                                            case CursorType.IDC_HAND:
                                                newcur = Cursors.Hand;
                                                break;
                                            case CursorType.IDC_HELP:
                                                newcur = Cursors.Help;
                                                break;
                                            case CursorType.IDC_IBEAM:
                                                newcur = Cursors.IBeam;
                                                break;
                                            //case CursorType.IDC_ICON:
                                            //    newcur = Cursors.No;
                                            //    break;
                                            case CursorType.IDC_NO:
                                                newcur = Cursors.No;
                                                break;
                                            case CursorType.IDC_SIZE:
                                                newcur = Cursors.SizeAll;
                                                break;
                                            case CursorType.IDC_SIZEALL:
                                                newcur = Cursors.SizeAll;
                                                break;
                                            case CursorType.IDC_SIZENESW:
                                                newcur = Cursors.SizeNESW;
                                                break;
                                            case CursorType.IDC_SIZENS:
                                                newcur = Cursors.SizeNS;
                                                break;
                                            case CursorType.IDC_SIZENWSE:
                                                newcur = Cursors.SizeNWSE;
                                                break;
                                            case CursorType.IDC_SIZEWE:
                                                newcur = Cursors.SizeWE;
                                                break;
                                            case CursorType.IDC_UPARROW:
                                                newcur = Cursors.UpArrow;
                                                break;
                                            case CursorType.IDC_WAIT:
                                                newcur = Cursors.WaitCursor;
                                                break;
                                            default:
                                                break;
                                        }

                                        virtualDesktopSyncDic["DeskForm"].pictureBox1.Cursor = newcur;
                                    }
                                    else { continue; }


                                }
                                break;

                            case InputEvent.ClipboardUpdated:
                                {
                                    if (Clipboard == ClipboardMode.Disabled
                                        || Clipboard == ClipboardMode.Send)
                                    {
                                        continue;

                                    }

                                    HostSyncHash.Clear();
                                    HostSyncHash.Add("ClipboardText", jsonobj.Text);


                                }

                                break;
                            case InputEvent.DesktopActive:
                                break;
                            case InputEvent.DesktopInactive:
                                break;
                            default:
                                break;
                        }
                    }
                    catch (Exception)
                    {

                        //throw;
                        continue;
                    }


                }
            }
            );
            IngressEventUpdater.Start();


        }

        public void EgressEventUpdater(Hashtable virtualDesktopSyncHash, ClipboardMode Clipboard)
        {
            Task EgressEventUpdater = new Task(() =>
            {

                Stopwatch stopWatch = Stopwatch.StartNew();
                while (true)
                {
                    if (stopWatch.ElapsedMilliseconds >= 1000)
                    {
                        try
                        {
                            bool eventTriggered = false;

                            if (!eventTriggered)
                            {
                                //object dd = new { };
                                object dd = null;

                                if (!sendEvent(OutputEvent.KeepAlive, dd))
                                {
                                    break;
                                }

                            }
                        }
                        catch (Exception)
                        {

                            throw;
                        }
                        finally
                        {
                            stopWatch.Restart();
                        }
                    }
                }
            });
            EgressEventUpdater.Start();
        }



        public void VirtualDeskFormIO_closing(object sender, FormClosingEventArgs e)
        {
            this.session.CloseSession();
        }

        public PSCustomObject MouseEvent(int X, int Y, MouseState type, string button = "")
        {
            var CustomObject = new PSCustomObject();
            CustomObject.Id = (int)OutputEvent.MouseClickMove;
            CustomObject.X = X;
            CustomObject.Y = Y;
            CustomObject.Button = button;
            CustomObject.Type = type;
            return CustomObject;
        }

        public PSCustomObject KeyboardEvent(string Keys = "")
        {
            var CustomObject = new PSCustomObject();
            CustomObject.Id = (int)OutputEvent.Keyboard;

            CustomObject.Keys = Keys;
            return CustomObject;
        }

        public bool sendEvent(OutputEvent oee, object data)
        {
            try
            {
                if (data == null)
                {
                    data = new { Id = (int)oee };
                }
                else
                {
                }
                data = new { Id = (int)oee };
                //session.ClientEvents.WriteJson(data);
                outputEventSyncDic["outputEvent"].WriteJson(data);

                return true;
            }
            catch (Exception error)
            {
                MessageBox.Show(error.Message, "sendEvent Exception");
                return false;
                //throw;
            }

        }

        public void SendVirtualMouse(int X, int Y, MouseState type, string button = "")
        {
            int xX=0;
            int yY = 0;

            if (session.ViewerConfiguration.RequireResize)
            {
               xX =(int) (X * session.ViewerConfiguration.ScreenX_Ratio);
               yY = (int) (Y * session.ViewerConfiguration.ScreenY_Ratio);
            }

            xX += session.ViewerConfiguration.ScreenX_Delta;
            yY += session.ViewerConfiguration.ScreenY_Delta;

            //PSCustomObject temp = MouseEvent(X, Y, type, button);
            object temp = new
            {
                Id = (int)OutputEvent.MouseClickMove,  ///simulate mouse move and clicks
                X = xX,
                Y = yY,
                Type = (int)type,
                Button = button,
            };
            try
            {
                //outputEventSyncHash.WriteJson(temp);
                //session.ClientEvents.WriteJson(temp);
                outputEventSyncDic["outputEvent"].WriteJson(temp);

            }
            catch (Exception error)
            {
                MessageBox.Show(error.Message, "SendVirtualMouse Exception");
            }
        }

        public void SendVirtualKeyboard(string Keys = "")
        {
            object temp = new { Id = (int)OutputEvent.Keyboard, Keys = Keys };

            try
            {
                //session.ClientEvents.WriteJson(temp);
                outputEventSyncDic["outputEvent"].WriteJson(temp);
            }
            catch (Exception error)
            {
                MessageBox.Show(error.Message, "SendVirtualKeyboard Exception");
            }
        }

        public void logic_MouseDown(object sender, MouseEventArgs e)
        {
            SendVirtualMouse(e.X, e.Y, MouseState.Down, e.Button.ToString());
        }

        public void logic_MouseUp(object sender, MouseEventArgs e)
        {
            SendVirtualMouse(e.X, e.Y, MouseState.Up, e.Button.ToString());

        }

        public void logic_MouseMove(object sender, MouseEventArgs e)
        {
            SendVirtualMouse(e.X, e.Y, MouseState.Move, e.Button.ToString());

        }

        public void logic_MouseWheel(object sender, MouseEventArgs e)
        {
            object temp = new
            {
                Id = (int)OutputEvent.MouseWheel,
                Delta = e.Delta
            };
            try
            {
                //session.ClientEvents.WriteJson(temp);
                outputEventSyncDic["outputEvent"].WriteJson(temp);

            }
            catch (Exception error)
            {
                MessageBox.Show(error.Message, "SendMouseWheel Exception");

                //throw;
            }

        }

        public void logic_DoubleClick(object sender, EventArgs e)
        {
        }

        public void logic_KeyPress(object sender, KeyPressEventArgs e)
        {
            string keychar = "";
            switch (e.KeyChar.ToString())
            {
                case "{":
                    keychar = "{{}";
                    break;
                case "}":
                    keychar = "{}}";
                    break;
                case "+":
                    keychar = "{+}";
                    break;
                case "^":
                    keychar = "{^}";
                    break;
                case "%":
                    keychar = "{%}";
                    break;
                case "~":
                    keychar = "{~}";
                    break;
                case "(":
                    keychar = "{(}";
                    break;
                case ")":
                    keychar = "{)}";
                    break;

                case "[":
                    keychar = "{[}";
                    break;
                case "]":
                    keychar = "{]}";
                    break;

                default:
                    keychar = e.KeyChar.ToString();
                    break;
            }

            SendVirtualKeyboard(keychar);
            Trace.Write(string.Format("{0}{1}{0}","\"",keychar)); //log
            Trace.Write(","); //log
            Trace.Close();


        }

        public void logic_KeyUp(object sender, KeyEventArgs e)
        {

        }

        public void logic_KeyDown(object sender, KeyEventArgs e)
        {
            string res = "";
            if (Map.funcKeymap.ContainsKey(e.KeyValue))
            {
                res = Map.funcKeymap[e.KeyValue];
            }

            if (!string.IsNullOrEmpty(res))
            {
                SendVirtualKeyboard(res);
                Trace.Write(string.Format(" {0}{1}{0}", "\"", res)); //log
                Trace.Write(","); //log
                Trace.Close();
            }
        }

        public void logic_Leave(object sender, EventArgs e)
        {

        }


        public void logic_FormShown(object sender, EventArgs e)
        {
            //Center Virtual Desktop Form
            VirtualDeskForm.Location = new Point(
                           (clientFunc.GetLocalScreenWidth() - VirtualDeskForm.Width) / 2,
                           (clientFunc.GetLocalScreenHeight() - VirtualDeskForm.Height) / 2
                       );

            VirtualDeskForm.TopMost = AlwaysOnTop;
        }









    }
}

